# -*- coding: utf-8 -*-

"""剑指 Offer II 071. 按权重生成随机数
给定一个正整数数组 w ，其中 w[i] 代表下标 i 的权重（下标从 0 开始），请写一个函数 pickIndex ，它可以随机地获取下标 i，选取下标 i 的概率与 w[i] 成正比。
例如，对于 w = [1, 3]，挑选下标 0 的概率为 1 / (1 + 3) = 0.25 （即，25%），而选取下标 1 的概率为 3 / (1 + 3) = 0.75（即，75%）。
也就是说，选取下标 i 的概率为 w[i] / sum(w) 。

示例 1：
输入：
inputs = ["Solution","pickIndex"]
inputs = [[[1]],[]]
输出：
[null,0]
解释：
Solution solution = new Solution([1]);
solution.pickIndex(); // 返回 0，因为数组中只有一个元素，所以唯一的选择是返回下标 0。

示例 2：
输入：
inputs = ["Solution","pickIndex","pickIndex","pickIndex","pickIndex","pickIndex"]
inputs = [[[1,3]],[],[],[],[],[]]
输出：
[null,1,1,1,1,0]
解释：
Solution solution = new Solution([1, 3]);
solution.pickIndex(); // 返回 1，返回下标 1，返回该下标概率为 3/4 。
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 1
solution.pickIndex(); // 返回 0，返回下标 0，返回该下标概率为 1/4 。

由于这是一个随机问题，允许多个答案，因此下列输出都可以被认为是正确的:
[null,1,1,1,1,0]
[null,1,1,1,1,1]
[null,1,1,1,0,0]
[null,1,1,1,0,1]
[null,1,0,1,0,0]
......
诸若此类。

提示：
1 <= w.length <= 10000
1 <= w[i] <= 10^5
pickIndex 将被调用不超过 10000 次"""

from random import randint

class Solution:
    """观念转变，按权重取下标，可以按总量sum(w)取随机数，然后不同权重给予不同的捕获范围，这样权重大的捕获范围大，更容易命中。
    具体实现时，计算前缀和（天然有序递增），二分查找大于或等于随机数的第一个前缀和元素"""
    def __init__(self, w):
        pre_sums_w = [w[0]]
        try:
            i = 1
            while True:
                pre_sums_w.append(pre_sums_w[-1]+w[i])
                i += 1
        except IndexError:
            pass

        self.pre_sums_w = pre_sums_w

        self.sums = pre_sums_w[-1]
        self.tail = len(self.pre_sums_w)-1

    def pickIndex(self) -> int:
        r = randint(1, self.sums)
        
        if r <= self.pre_sums_w[0]:
            return 0
        mini, maxi = 0, self.tail
        while maxi-mini > 1:
            midd = (mini+maxi)//2
            if self.pre_sums_w[midd] >= r:
                maxi = midd
            else:
                mini = midd
        
        return maxi


if __name__ == '__main__':
    solution = Solution([1, 3])
    print(solution.pickIndex())
    print(solution.pickIndex())
    print(solution.pickIndex())
    print(solution.pickIndex())
    print(solution.pickIndex())

